﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data.SqlClient;
using System.Data;
using System.Xml;
using System.Collections;

namespace ProcedureAuto
{
    class Program
    {
		private static IDictionary<String, String> config;
	
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("正在读取config.xml...");
                string configFileName = "config.xml";
                config = new Config().LoadConfig(configFileName);
                if (config == null)
                {
                    Console.WriteLine("config.xml不存在！");
                    Console.ReadKey();
                    return;
                }
                Console.WriteLine("config.xml读取成功");

                Console.WriteLine("正在获取所有存储过程名...");
                SqlConnection conn = new SqlConnection(config["ConnectionString"]);
                conn.Open();
                SqlCommand command = new SqlCommand();
                command.CommandText = "select name from sysobjects where xtype = 'P' and name like 'Pro_%'";
                command.Connection = conn;
                SqlDataAdapter adp = new SqlDataAdapter(command);
                DataTable dt = new DataTable();
                adp.Fill(dt);
                conn.Close();
                Console.WriteLine("正在进行procedure.xml更新...");
                if (SetProcedureXML(dt) == false)
                {
                    Console.WriteLine("procedure.xml写入出错！");
                    Console.ReadKey();
                    return;
                }
                Console.WriteLine("procedure.xml更新结束");

                StreamWriter ProcedureService = new StreamWriter(@config["ServerPath"] + @"\Dao\ProcedureService.cs");
                StreamWriter ProcessDaoRequest = new StreamWriter(@config["ServerPath"] + @"\Net\ProcessDaoRequest.cs");
                StreamWriter ClientProcedureService = new StreamWriter(@config["ClientNetPath"] + @"\ClientProcedureService.cs");
                StreamWriter ProcedureResultParser = new StreamWriter(@config["ClientNetPath"] + @"\ProcedureResultParser.cs");

                String ProcedureService_String = "";
                String ProcessDaoRequest_String = "";
				String ClientProcedureService_String = "";
				String ProcedureResultParser_String = "";

                int code = 2000;
                foreach (DataRow dr in dt.Rows)
                {
                    string procedureName = dr[0].ToString();
                    bool select = false;
                    if (procedureName.Contains("Pro_Get")) select = true;

                    command = conn.CreateCommand();
                    command.CommandText = procedureName;
                    command.CommandType = CommandType.StoredProcedure;
                    conn.Open();
                    SqlCommandBuilder.DeriveParameters(command);
                    ProcedureParameter[] procedureParameters = new ProcedureParameter[command.Parameters.Count - 1];
                    int index = 0;
                    foreach (SqlParameter item in command.Parameters)
                    {
                        if (item.ParameterName != "@RETURN_VALUE") procedureParameters[index++] = new ProcedureParameter(item.ParameterName, item.ParameterName.Replace("@", ""), ConvertType(item.SqlDbType), item.SqlDbType.ToString());
                    }
                    conn.Close();

                    ProcedureService_String += "\t\tpublic void " + procedureName + "(Object[] parameters, int userLevel, ";
                    if (select) ProcedureService_String += "Delegate_DataTableReach dataTableReach)\r\n\t\t{\r\n";
                    else ProcedureService_String += "Delegate_AffectedRowsReach affectedRowsReach)\r\n\t\t{\r\n";
                    ProcedureService_String += "\t\t\tif (procedureConfig[\"" + procedureName + "\"] < userLevel) { LogService.Instance.Log.Error(\"userlevel is not enough to access this procedure!\"); return; }\r\n";
                    if (select) ProcedureService_String += "\t\t\tthis.dataTableReach = dataTableReach;\r\n";
                    else ProcedureService_String += "\t\t\tthis.affectedRowsReach = affectedRowsReach;\r\n";
                    ProcedureService_String += "\t\t\t" + procedureName + "(";
                    for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ProcedureService_String += "(" + procedureParameters[i].TypeName + ")parameters[" + i + "]";
                        if (i != procedureParameters.Length - 1) ProcedureService_String += ", ";
                    }
                    ProcedureService_String += ");\r\n\t\t}\r\n\r\n";
                    ProcedureService_String += "\t\tprivate void " + procedureName + "(";
                    for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ProcedureService_String += procedureParameters[i].TypeName + " " + procedureParameters[i].PName;
                        if (i != procedureParameters.Length - 1) ProcedureService_String += ", ";
                    }
                    ProcedureService_String += ")\r\n\t\t{\r\n";
                    ProcedureService_String += "\t\t\tSqlConnection conn = new DBConnection().GetConnection();\t\n";
                    ProcedureService_String += "\t\t\tSqlCommand command = new SqlCommand();\r\n";
                    ProcedureService_String += "\t\t\tcommand.Connection = conn;\r\n";
                    ProcedureService_String += "\t\t\tcommand.CommandText = \"" + procedureName + "\";\r\n";
                    ProcedureService_String += "\t\t\tcommand.CommandType = CommandType.StoredProcedure;\r\n";
                    for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ProcedureService_String += "\t\t\tcommand.Parameters.Add(\"" + procedureParameters[i].ParameterName + "\", SqlDbType." + procedureParameters[i].SqlDBType + ").Value = " + procedureParameters[i].PName + ";\r\n";
                    }
                    ProcedureService_String += "\t\t\tAsyncCallback ac = new AsyncCallback(" + procedureName + "Callback);\r\n\t\t\tconn.Open();\r\n";
                    if (select) ProcedureService_String += "\t\t\tcommand.BeginExecuteReader(ac, command);\r\n\t\t}\r\n\r\n";
                    else ProcedureService_String += "\t\t\tcommand.BeginExecuteNonQuery(ac, command);\r\n\t\t}\r\n\r\n";
                    ProcedureService_String += "\t\tprivate void " + procedureName + "Callback(IAsyncResult ar)\r\n\t\t{\r\n";
                    ProcedureService_String += "\t\t\tSqlCommand command = (SqlCommand)ar.AsyncState;\r\n";
                    if (select) ProcedureService_String += "\t\t\tSqlDataReader sDR = command.EndExecuteReader(ar);\r\n\t\t\tDataTable dt = new DataTable();\r\n\t\t\tdt.Load(sDR);\r\n\t\t\tdataTableReach(dt);\r\n\t\t}\r\n\r\n";
                    else ProcedureService_String += "\t\t\tint affectRows = command.EndExecuteNonQuery(ar);\r\n\t\t\taffectedRowsReach(affectRows);\r\n\t\t}\r\n\r\n";

					
					
                    ProcessDaoRequest_String += "\t\t\t\t\tcase \"" + code + "\":\r\n";
                    ProcessDaoRequest_String += "\t\t\t\t\t\tnew ProcedureService()." + procedureName + "(parameters, token.UserLevel, new ";
                    if (select) ProcessDaoRequest_String += "Delegate_DataTableReach(DataTableCallback));\r\n";
                    else ProcessDaoRequest_String += "Delegate_AffectedRowsReach(AffectedRowsCallback));\r\n";
                    ProcessDaoRequest_String += "\t\t\t\t\t\tbreak;\r\n";
					
					
					
					ClientProcedureService_String +="\t\tpublic " + (select?"DataTable ":"int ") + procedureName + "(";
					for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ClientProcedureService_String += procedureParameters[i].TypeName + " " + procedureParameters[i].PName;
						if(i != procedureParameters.Length - 1) ClientProcedureService_String += ", ";
                    }
					ClientProcedureService_String += ")\r\n\t\t{\r\n";
					ClientProcedureService_String += "\t\t\tNetCallback netCallback = new NetCallback();\r\n";
					ClientProcedureService_String += "\t\t\tnetCallback.DataReach = new ManualResetEvent(false);\r\n";
					ClientProcedureService_String += "\t\t\tnetCallback.State = NetCallback.Block;\r\n";
					ClientProcedureService_String += "\t\t\tNetService.callbackMap[NetService.net.PackageIdentifier] = netCallback;\r\n";
					ClientProcedureService_String += "\t\t\tobject[] parameters = new object[]{";
					for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ClientProcedureService_String += procedureParameters[i].PName;
						if(i != procedureParameters.Length - 1) ClientProcedureService_String += ", ";
                    }
					ClientProcedureService_String += "};\r\n\t\t\tbyte[] b = ByteArrayParser.ObjectToByteArray(parameters);\r\n";
					ClientProcedureService_String += "\t\t\tNetService.net.ProcessSend(\"" + code + "\", b);\r\n";
					ClientProcedureService_String += "\t\t\tnetCallback.DataReach.WaitOne(3000);\r\n";
					ClientProcedureService_String += "\t\t\treturn (" + (select?"DataTable":"int") + ")netCallback.AsyncObject;\r\n\t\t}\r\n\r\n";
					ClientProcedureService_String +="\t\tpublic void " + procedureName + "(";
					for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ClientProcedureService_String += procedureParameters[i].TypeName + " " + procedureParameters[i].PName + ", ";
                    }
                    ClientProcedureService_String += (select ? "DataTable" : "Int") + "Callback " + (select ? "dataTable" : "int") + "Callback)\r\n\t\t{\r\n";
					ClientProcedureService_String += "\t\t\tNetCallback netCallback = new NetCallback();\r\n";
					ClientProcedureService_String += "\t\t\tnetCallback." + (select?"dataTable":"int") + "Callback = " + (select?"dataTable":"int") + "Callback;\r\n";
					ClientProcedureService_String += "\t\t\tnetCallback.State = NetCallback.Wait" + (select?"DataTable":"Int") + ";\r\n";
					ClientProcedureService_String += "\t\t\tNetService.callbackMap[NetService.net.PackageIdentifier] = netCallback;\r\n";
					ClientProcedureService_String += "\t\t\tobject[] parameters = new object[] {";
					for (int i = 0; i < procedureParameters.Length; i++)
                    {
                        ClientProcedureService_String += procedureParameters[i].PName;
						if(i != procedureParameters.Length - 1) ClientProcedureService_String += ", ";
                    }
					ClientProcedureService_String += "};\r\n\t\t\tbyte[] b = ByteArrayParser.ObjectToByteArray(parameters);\r\n";
					ClientProcedureService_String += "\t\t\tNetService.net.ProcessSend(\"" + code + "\", b);\r\n\t\t}\r\n\r\n";
					
					
					
					ProcedureResultParser_String += "\t\t\t\tcase \"" + (code+1) + "\":\r\n";
                    ProcedureResultParser_String += "\t\t\t\t\t" + (select ? "DBDataReach()" : "AffectedRowsReach()") + ";\r\n";
					ProcedureResultParser_String += "\t\t\t\t\tbreak;\r\n";

                    code += 2;
                }

                FileInfo ProcessDaoRequest_Txt = new FileInfo("ProcessDaoRequest.txt");
                StreamReader sr = ProcessDaoRequest_Txt.OpenText();
                String template = sr.ReadToEnd();
                string result = template.Replace("{0}", ProcessDaoRequest_String);
                ProcessDaoRequest.Write(result);
                ProcessDaoRequest.Close();
                sr.Close();

                FileInfo ProcedureService_Txt = new FileInfo("ProcedureService.txt");
                sr = ProcedureService_Txt.OpenText();
                template = sr.ReadToEnd();
                result = template.Replace("{0}", ProcedureService_String);
                ProcedureService.Write(result);
                ProcedureService.Close();
                sr.Close();
				
				FileInfo ClientProcedureService_Txt = new FileInfo("ClientProcedureService.txt");
                sr = ClientProcedureService_Txt.OpenText();
                template = sr.ReadToEnd();
                result = template.Replace("{0}", ClientProcedureService_String);
                ClientProcedureService.Write(result);
                ClientProcedureService.Close();
                sr.Close();
				
				FileInfo ProcedureResultParser_Txt = new FileInfo("ProcedureResultParser.txt");
                sr = ProcedureResultParser_Txt.OpenText();
                template = sr.ReadToEnd();
                result = template.Replace("{0}", ProcedureResultParser_String);
                ProcedureResultParser.Write(result);
                ProcedureResultParser.Close();
                sr.Close();

                
                Console.WriteLine("解析存储过程完成，按任意键结束....");
                Console.ReadKey();
            }
            catch (Exception e)
            {
                Console.WriteLine("程序出错终止了！  " + e.ToString());
                Console.ReadKey();
            }
        }
		

        private static bool SetProcedureXML(DataTable dt)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(@config["ServerPath"] + @"\Dao\procedure.xml");
                XmlNodeList nodeList = doc["procedures"].ChildNodes;
                ArrayList xmlNodeArray = new ArrayList();
                foreach (XmlNode node in nodeList)
				{
					foreach(DataRow dr in dt.Rows)
					{
                        if (node.Attributes["name"].Value.ToString() == dr[0].ToString()) xmlNodeArray.Add(node);
					}
				}
                doc["procedures"].RemoveAll();
                for (int i = 0; i < xmlNodeArray.Count; i++)
                {
                    doc["procedures"].AppendChild((XmlNode)xmlNodeArray[i]);
                }
				foreach(DataRow dr in dt.Rows)
				{
					bool needToAddProcedure = true;
                    foreach (XmlNode node in doc["procedures"].ChildNodes)
					{
						if(node.Attributes["name"].Value.ToString() == dr[0].ToString()) needToAddProcedure = false;
					}
					if(needToAddProcedure == true)
					{
                        Console.WriteLine("procedure.xml新增节点" + dr[0].ToString());
						XmlElement xe = doc.CreateElement("procedure");
						XmlAttribute xmlAttribute = doc.CreateAttribute("name");
                        xmlAttribute.Value = dr[0].ToString();
                        xe.Attributes.Append(xmlAttribute);
						xmlAttribute = doc.CreateAttribute("level");
						xmlAttribute.Value = "-1";
						xe.Attributes.Append(xmlAttribute);
						doc["procedures"].AppendChild(xe);
					}
				}
                doc.Save(@config["ServerPath"] + @"\Dao\procedure.xml");
                return true;
            }
            catch
            {
                return false;
            }
        }

        private static string ConvertType(SqlDbType dbType)
        {
            string str = "";
            switch (dbType.ToString())
            {
                case "Int": str = "int"; break;
                case "VarChar": str = "string"; break;
                case "NVarChar": str = "string"; break;
                case "Bit": str = "bool"; break;
                case "Decimal": str = "float"; break;
                case "Float": str = "float"; break;
                default:
                    Console.WriteLine("未识别类型：" + dbType.ToString());
                    break;
            }
            return str;
        }
    }

    class ProcedureParameter
    {
        public string ParameterName;
        public string PName;
        public string TypeName;
        public string SqlDBType;
        public ProcedureParameter(string s1, string s2, string s3, string s4)
        {
            ParameterName = s1;
            PName = s2;
            TypeName = s3;
            SqlDBType = s4;
        }
    }


}
